package com.nsoft.pdfrender;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import nl.siegmann.epublib.domain.Author;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.Date;
import nl.siegmann.epublib.domain.Identifier;
import nl.siegmann.epublib.domain.Metadata;
import nl.siegmann.epublib.domain.Resource;
import nl.siegmann.epublib.epub.EpubWriter;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;

import com.adobe.epubcheck.api.EpubCheck;
import com.adobe.epubcheck.util.EPUBVersion;

@SuppressWarnings("unchecked")
public class TextUtils {
	static final String EMPTY = "";
	static final String LINEBREAK = "\r\n";
	
	static String parentDirPath="";
	static String tempDirName="";
	static String tempDirPath="";
	static String xhtmlName="";
	static String xhtmlFileName="";
	static String xhtmlFilePath="";
	static PdfImage coverImage;
	static List<PdfImage> bookImages=new ArrayList<PdfImage>();
	
	static boolean isStartTag=false;
	
	public static String getParentDirPath(){
		return parentDirPath;
	}
	
	public static String getTempDirName(){
		return tempDirName;
	}
	
	public static String getTempDirPath(){
		return tempDirPath;
	}
	
	public static void convertToEpub(File txtFile) throws IOException {
		try{
			// ========== CONVERT TO XHTML ========== 
			convertToXhtml(txtFile);

			// ========== CONVERT TO EPUB ========== 
			parseToEpub(txtFile);

		}catch(IOException e){
			throw e;
		}finally{
			// ========== CLEAN THE TEMP FOLDER ========== 
			Utils.delete(new File(tempDirPath));
		}
	}
	
	private static void parseToEpub(File txtFile) throws IOException {
		String fileNameNoExt=txtFile.getName().substring(0,txtFile.getName().length()-4);
		
		// ========== CONVERT TO EPUB ========== 
		Book epubBook=new Book();
		
		// ========== METADATA ========== 
		// set reflowable layout
		epubBook.setBookType(1);
		Metadata meta=epubBook.getMetadata();
		// set identifier
		Identifier ident=new Identifier();
		ident.setValue(UUID.randomUUID().toString());
		meta.addIdentifier(ident);
		// set title
		String title=txtFile.getName().substring(0,txtFile.getName().length()-4);
		meta.addTitle(title);
		// set author
		meta.addAuthor(new Author(Utils.DEF_AUTHOR));
		// set date
		meta.addDate(new Date(new java.util.Date()));
		// set description
		meta.addDescription(txtFile.getName());
		// set publisher
		meta.addPublisher(Utils.DEF_AUTHOR);
		// set language
		//meta.setLanguage(Utils.DEF_LANGUAGE);
		
		// ========== BOOK COVER ========== 
		if (coverImage!=null && coverImage.getPath()!=null){
			byte[] coverImageData=Utils.readBinaryFile(coverImage.getPath());
			String coverExt=coverImage.getName().
					substring(coverImage.getName().length()-3);
			Resource coverRes=new Resource(coverImageData,"cover."+coverExt);
			coverRes.setId("cover");
			epubBook.setCoverImage(coverRes);
		}
		
		// ========== REMAIN IMAGES ========== 
		byte[] resData;
		Resource resource;
		String resId=Utils.EMPTY;
		for (PdfImage img:bookImages){
			resData=Utils.readBinaryFile(img.getPath());
			resId=Utils.IMAGE_PREFIX+img.getName().substring(0,img.getName().length()-4);
			resource=new Resource(resData,img.getName());
			resource.setId(resId);
			epubBook.addResource(resource);
		}
		
		// ========== XHTML FILES ========== 
		resData=Utils.readBinaryFile(xhtmlFilePath);
		resource=new Resource(resData,xhtmlFileName);
		resource.setId(xhtmlName);
		epubBook.addSection(xhtmlName,resource);
		
		// ========== NAV HTML FILE ========== 
		String navData=Utils.createXhtmlNav(xhtmlName,xhtmlFileName);
		resource=new Resource(navData.getBytes("UTF-8"),"nav.html");
		resource.setId("nav");
		epubBook.setNavResource(resource);
		
		// ========== GENERATE EPUB FILE ==========
		try {
			String epubOutputPath=parentDirPath+"/"+fileNameNoExt+".epub";
			EpubWriter epubWriter=new EpubWriter();
			epubWriter.write(epubBook,
				new FileOutputStream(epubOutputPath));
			
			// ========== VALIDATE EPUB ========== 
			EpubReporter reporter=new EpubReporter();
			EpubCheck checker=new EpubCheck(
						new File(epubOutputPath),
						reporter,
						EPUBVersion.VERSION_3);
			
			// if the validation is failed, the epub file will be erased
			boolean isEpubValid=checker.validate();
			if (!isEpubValid){
				Utils.delete(new File(epubOutputPath));
				throw new IOException(reporter.getErrorMessage());
			}
			
		}catch(IOException e){
			throw e;
		}
	}
	
	private static void convertToXhtml(File txtFile) throws IOException {
		String xhtml=convertToHtml(txtFile);
		xhtmlName="ch01_t";
		xhtmlFileName=xhtmlName+".xhtml";
		xhtmlFilePath=tempDirPath+"/"+xhtmlFileName;
		Utils.saveToFile(xhtmlFilePath,xhtml);
	}
	
	private static String convertToHtml(File txtFile) throws IOException {
		StringBuffer buff=new StringBuffer();
		
		// create the temporary folder to store temporary files
		// like images, css, ... for generating EPUB
		createTempFolder(txtFile);
		
		// get the corresponding PDF file
		String pdfFilePath=txtFile.getAbsolutePath().substring(0,
				txtFile.getAbsolutePath().length()-3)+"pdf";
		File pdfFile=new File(pdfFilePath);
		if (!pdfFile.exists()){
			// if the pdf is not available, then skip the header info
			// and cover image
			
			System.err.println("txt-module: pdf file is not available.");
			
			// attach the definition and header of the html document
			String docName=txtFile.getName().
					substring(0,txtFile.getName().length()-4);
			// use the text file name as the title name of the html
			buff.append(Utils.getPageHeader(docName));
		}else{
			PDDocument doc=PDDocument.load(pdfFilePath);
			PDDocumentInformation pdfInfo=doc.getDocumentInformation();
			
			// attach the definition and header of the html document
			buff.append(Utils.getPageHeader(pdfInfo.getTitle()));
			
			// detect cover image in the pdf
			// only search the image in 5 first pages
			PDDocumentCatalog pdfCatalog=doc.getDocumentCatalog();
			List<PDPage> pages=pdfCatalog.getAllPages();
			PDPage page;
			PdfImage[] imageList;
			coverImage=null;
			bookImages=new ArrayList<PdfImage>();
			boolean coverFound=false;
			for (int i=0;i<5;i++){
				page=pages.get(i);
				// add images to page
				imageList=getImages(page,i);
				if (!coverFound && imageList.length>0){
					// add the first image as the cover image
					coverImage=imageList[0];
					// image cover is found, no longer search
					coverFound=!coverFound;
				}
				for (int j=0;j<imageList.length;j++){
					buff.append(imageList[j].getImageTag()+Utils.LINEBREAK);
					bookImages.add(imageList[j]);
				}
			}
			doc.close();
		}
		
		// analyze text
		StringBuffer bookBuff=Utils.readFile(txtFile);
		String[] bookLines=bookBuff.toString().split(Utils.LINEBREAK);
		PdfParagraph pBuff=new PdfParagraph();
		for (int i=0;i<bookLines.length;i++){
			// convert the line to html form
			pBuff.append(Utils.cleanToXhmtl(bookLines[i]));
			// and add to html page
			buff.append(pBuff.getHtml()+Utils.LINEBREAK);
			pBuff=new PdfParagraph();
		}
		
		
		// attach the footer of html document
		buff.append(Utils.getPageFooter());
		
		return buff.toString();
	}
	
	private static PdfImage[] getImages(PDPage page,int pIdx) throws IOException {
		ArrayList<PdfImage> outImgList=new ArrayList<PdfImage>();
		Map<String,PDXObjectImage> imageList=page.getResources().getImages();
		PDXObjectImage imgObj;
		int cnt=1;
		String imgTempName,imgFullTempName=Utils.EMPTY,
			imgTempPath,imgFullTempPath=Utils.EMPTY;
		for (String k:imageList.keySet()){
			imgObj=imageList.get(k);
			imgTempName=pIdx+"_"+cnt;
			//imgFullTempName=imgTempName+".jpg";
			imgTempPath=tempDirPath+"/"+imgTempName;
			//imgFullTempPath=tempDirPath+"/"+imgFullTempName;
			imgObj.write2file(imgTempPath);
			
			for (int i=0;i<Utils.IMAGE_EXTENSIONS.length;i++){
				//imgFullTempName=imgTempName+"."+Utils.IMAGE_EXTENSIONS[i];
				imgFullTempPath=tempDirPath+"/"+imgTempName+"."+Utils.IMAGE_EXTENSIONS[i];
				if (new File(imgFullTempPath).exists()){
					imgFullTempName=imgTempName+"."+Utils.IMAGE_EXTENSIONS[i];
					break;
				}
			}
			
			if (!imgFullTempName.equals(Utils.EMPTY)){
				outImgList.add(new PdfImage(imgFullTempName,imgFullTempPath,
							imgObj.getWidth(),imgObj.getHeight()));
			}
			
			cnt++;
		}
		return outImgList.toArray(new PdfImage[]{});
	}
		
	private static void createTempFolder(File pdfFile) throws IOException {
		parentDirPath=pdfFile.getParent();
		
		String fileName=pdfFile.getName();
		tempDirName=fileName.substring(0,fileName.length()-4)+"_tmp";
		tempDirPath=parentDirPath+"/"+tempDirName;
		
		// remove the temp directory
		File fTmpDir=new File(tempDirPath);
		if (fTmpDir.exists()){
			Utils.delete(fTmpDir);
		}
		
		// remove the epub file
		File epubFile=new File(parentDirPath+"/"+
					fileName.substring(0,fileName.length()-4)+".epub");
		if (epubFile.exists()){
			Utils.delete(epubFile);
		}
		
		new File(tempDirPath).mkdir();
	}
}
